package com.test.common.utils;

import com.test.common.entity.perf.PerfReportEntity;
import com.test.common.enums.perf.PerfScriptPlatformEnum;
import com.test.common.response.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Slf4j
public class ScriptUtils {

    public void modifyScript(PerfScriptPlatformEnum platform, File scriptFile, PerfReportEntity perfReportEntity) {
        log.info("开始修改脚本文件：{}，{}", scriptFile, platform);
        switch (platform) {
            case JMETER:
                modifyJmeterScript(scriptFile, perfReportEntity);
                break;
            case GATLING:
                modifyGatlingScript(scriptFile, perfReportEntity);
                break;
            default:
                throw new BizException("不支持的脚本平台：" + platform);
        }
    }

    /**
     * 修改脚本的并发设置，启动时间，持续时间，给脚本加上相关
     * 修改Jmeter脚本
     *
     * @param scriptFile       脚本文件
     * @param perfReportEntity 配置信息
     */
    private void modifyJmeterScript(File scriptFile, PerfReportEntity perfReportEntity) {
        log.info("修改Jmeter脚本：{}", scriptFile.getAbsolutePath());
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(scriptFile);

            // 获取线程组节点，修改并发，每个线程组都要修改
            List<Node> nodes = document.selectNodes("//ThreadGroup[@enabled='true']");
            for (Node node : nodes) {

                // 获取启动时间节点，修改
                // 获取持续时间节点，修改

                Element loopControllerEle = (Element) node
                        .selectSingleNode("./elementProp[@name='ThreadGroup.main_controller']");
                Element delEle = (Element) node
                        .selectSingleNode("./elementProp/stringProp[@name='LoopController.loops']");
                loopControllerEle.remove(delEle);
                Element addEle = loopControllerEle.addElement("intProp");
                addEle.addAttribute("name", "LoopController.loops");
                addEle.setText("-1");

                // 设置并发
                node.selectSingleNode("./stringProp[@name='ThreadGroup.num_threads']")
                        .setText(String.valueOf(perfReportEntity.getSetUsers()));

                // 设置启动时间
                node.selectSingleNode("./stringProp[@name='ThreadGroup.ramp_time']")
                        .setText(String.valueOf(perfReportEntity.getRampTime()));

                node.selectSingleNode("./boolProp[@name='ThreadGroup.scheduler']").setText("true");

                // 设置执行时间
                node.selectSingleNode("./stringProp[@name='ThreadGroup.duration']")
                        .setText(String.valueOf(perfReportEntity.getDuration()));
            }

            // 禁用掉脚本自身的所有qps控制器
            List<Node> constantThroughputTimerNodes = document.selectNodes("//ConstantThroughputTimer[@enabled='true']");
            for (Node node : constantThroughputTimerNodes) {
                Element constantThroughputTimerElement = (Element) node;
                constantThroughputTimerElement.addAttribute("enabled", "false");
            }

            // 如果设定的qps不为0，添加一个qps控制器
            if (perfReportEntity.getSetQps() > 0) {
                Node rootNode = document.selectSingleNode("/jmeterTestPlan/hashTree/hashTree");
                Element rootElement = (Element) rootNode;

                Element tmpConstantThroughputTimerElement = rootElement.addElement("ConstantThroughputTimer");
                tmpConstantThroughputTimerElement.addAttribute("guiclass", "TestBeanGUI");
                tmpConstantThroughputTimerElement.addAttribute("testclass", "ConstantThroughputTimer");
                tmpConstantThroughputTimerElement.addAttribute("testname", "常数吞吐量定时器");
                tmpConstantThroughputTimerElement.addAttribute("enabled", "true");

                Element doublePropElement = tmpConstantThroughputTimerElement.addElement("doubleProp");
                doublePropElement.addElement("name").setText("throughput");
                doublePropElement.addElement("value").setText(perfReportEntity.getSetQps() * 60 + ".0");
                doublePropElement.addElement("savedValue").setText("0.0");

                Element intPropElement = tmpConstantThroughputTimerElement.addElement("intProp");
                intPropElement.addAttribute("name", "calcMode");
                intPropElement.setText("1");

                // 不知道为什么，后面都需要带一个hashTree
                rootElement.addElement("hashTree");
            }

            // 美化会导致脚本中的shell错乱，所以不做美化
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(scriptFile));
            xmlWriter.write(document);
            xmlWriter.flush();
            xmlWriter.close();
        } catch (IOException | DocumentException e) {
            log.error("修改脚本配置失败:|{}", e.getMessage());
            throw new BizException("修改脚本配置失败：" + e.getMessage());
        }
    }

    /**
     * 修改脚本的并发设置，启动时间，持续时间，给脚本加上相关
     * 删除package一行
     * 修改Gatling脚本
     *
     * @param scriptFile       脚本文件
     * @param perfReportEntity 配置
     */
    private void modifyGatlingScript(File scriptFile, PerfReportEntity perfReportEntity) {
        // todo 修改脚本的并发设置，启动时间，持续时间，给脚本加上相关
        // 包名先删了
        log.info("修改Gatling脚本：{}", scriptFile.getAbsolutePath());
        try {
            BufferedReader reader = new BufferedReader(new FileReader(scriptFile));

            StringBuilder sb = new StringBuilder();
            String line = null;
            String setUp = "";
            boolean append = true;
            boolean durationImported = false; // import scala.concurrent.duration._
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("package")) {
                    log.info("移除package：{}", line);
                    continue;
                } else if (line.trim().startsWith("setUp")) {
                    // 从这里开始，先不写入sb了，单独放在一个字符串里面
                    append = false;
                } else if (line.trim().startsWith("import scala.concurrent.duration")) {
                    durationImported = true;
                }
                if (append) {
                    sb.append(line).append("\n");
                } else {
                    setUp = setUp + line + "\n";
                }
            }
            reader.close();
            log.info("删除旧文件：{}，{}", scriptFile.getAbsolutePath(), scriptFile.delete());

            // 替换最后一段字符串里面的参数
            if (!durationImported) {
                sb.append("import scala.concurrent.duration._\n");
            }
            sb.append(scalaM(setUp, perfReportEntity.getSetUsers(), perfReportEntity.getRampTime(), perfReportEntity.getDuration()));

            BufferedWriter writer = new BufferedWriter(new FileWriter(scriptFile));
            writer.write(sb.toString());
            writer.close();
            log.info("写入新文件：{}", scriptFile.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String scalaM(String setUp, int users, int rampTime, int duration) {
        // 先修改并发和启动时间
        while (setUp.contains(".inject")) {
            int index = setUp.indexOf(".inject");
            char[] lineCharts = setUp.toCharArray();
            int left = 0, right = 0, count = 0;
            for (int i = index; i < lineCharts.length; i++) {
                if (left == 0 && lineCharts[i] == '(') {
                    left = i;
                } else if (lineCharts[i] == '(') {
                    count++;
                } else if (count == 0 && lineCharts[i] == ')') {
                    right = i;
                    break;
                } else if (lineCharts[i] == ')') {
                    count--;
                }
            }

            String substring = setUp.substring(index, right + 1);
            setUp = StringUtils.replaceOnce(setUp, substring, "AAZXDERFGYHKGBMJNMKNVGJZZ");
        }
        setUp = StringUtils.replace(setUp, "AAZXDERFGYHKGBMJNMKNVGJZZ", ".inject(rampUsers(" + users + ").during(" + rampTime + " seconds))");
        log.info("替换启动时间和并发");

        // 然后设置最大执行时间
        if (setUp.contains("maxDuration")) {
            // 修改
            Pattern pattern = Pattern.compile("maxDuration\\((.*?)\\)");
            Matcher matcher = pattern.matcher(setUp);
            matcher.find();
            setUp = StringUtils.replace(setUp, matcher.group(1), duration + " seconds");
            log.info("修改最大执行时间");
        } else {
            // 添加
            int index = setUp.indexOf("setUp");
            char[] lineCharts = setUp.toCharArray();
            int left = 0, right = 0, count = 0;
            for (int i = index; i < lineCharts.length; i++) {
                if (left == 0 && lineCharts[i] == '(') {
                    left = i;
                } else if (lineCharts[i] == '(') {
                    count++;
                } else if (count == 0 && lineCharts[i] == ')') {
                    right = i;
                    break;
                } else if (lineCharts[i] == ')') {
                    count--;
                }
            }
            setUp = new StringBuffer(setUp).insert(right + 1, ".maxDuration(" + duration + " seconds)").toString();
            log.info("添加最大执行时间");
        }

        return setUp;
    }

    public static void main(String[] args) {
    }
}
